package org.chispa.api.parsers;

import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.chispa.api.parsers.StateRepository.State;
import org.chispa.api.util.BadSyntaxException;
import org.chispa.api.util.ExpressionLanguage;
import org.chispa.api.util.ExpressionLanguage.Type;
import org.chispa.api.util.NoSuitableVisitorException;

class HashMethodParserState extends NonInitialParserState{
	
	protected HashMethodParserState(ParserState prev) {
		super(prev);
	}
	
	protected static final String ENDS_TOO_SOON="The SPARQL expression did not end correctly!";
	protected String ORIG=ExpressionLanguage.PLAIN_HASH;
	protected String CHANGED=ExpressionLanguage.PROC_HASH;
	
	@Override
	public ParserState validateState()
	{
		return this;
	}
	
	@Override
	protected ParserState validateState(String text) throws NoSuchElementException, BadSyntaxException, NoSuitableVisitorException {
		Type type=ExpressionLanguage.getType(text);
		text=text.replace(ORIG,CHANGED);
		append(text);
		if(scan.hasNext())
		{
			return doValidate(type);
		}
		else
			throw new BadSyntaxException(ENDS_TOO_SOON);
	}
	
	private void cacheExpression(Type type) throws NoSuitableVisitorException, NoSuchElementException, BadSyntaxException
	{
		String preBuff=flush();
		boolean ready=false;
		int lbr=1;
		int rbr=0;
		int cntToken=0;
		String token;
		String expr = "";
		do{
			token=scan.next();
			cntToken++;
			if(isUnclosedLiteral(token))
			{
				repo.getStateFor(State.LITERAL, this).validateState(token);
			}
			else
			{
				if(token.equals(")"))
				{
					rbr++;
					if(rbr==lbr)
					{
						ready=true;
						expr=flush();
						if(cntToken==2 && type.equals(Type.STRING))
						{
							expr=ExpressionLanguage.unquote(expr);
							expr=listener.onSimpleHash(expr);
						}
						else if(!type.equals(Type.STRING))
						{
							Map<String,String>params=collectParameters(preBuff);
							expr=listener.onLogicalNumberHash(expr, type,params);
						}
						else
						{
							expr=listener.onLogicalHash(expr);
						}
					}
					else
						append(token);
				}
				
				else
				{
					if(token.equals("("))
					{
						lbr++;
					}
					append(token);
				}
				
			}
		}while(!ready);
		
		append(preBuff);
		append(expr);
		append(token);
	}
	
	protected ParserState doValidate(Type type) throws BadSyntaxException, NoSuchElementException, NoSuitableVisitorException
	{
		String text=scan.next();
		if(!text.equals("("))
			throw new BadSyntaxException("Hash method name must be followed by left bracket!");
		append(text);
		if(type.equals(Type.STRING))
		{
			text=scan.next();
			if(!ExpressionLanguage.isParameter(text))
				throw new BadSyntaxException("The first parameter of the hash method must be a variable!");
			append(text);
			text=scan.next();
			if(!text.equals(ExpressionLanguage.COMMA))
				throw new BadSyntaxException("Hash methods must have two parameters!");
			append(text);
		}
		cacheExpression(type);
		prev.setBuffer(buffer);
		return prev.validateState();
	}
	
	private Map<String,String> collectParameters(String data)
	{
		HashMap<String,String> params=new HashMap<String,String>();
		Pattern pattern=Pattern.compile(ExpressionLanguage.TRIPLE_REGEX);
		Matcher matcher=pattern.matcher(data);
		while(matcher.find())
		{
			String group=matcher.group().replaceAll("\\s+", " ");
			String[]tokens=group.split(" ");
			for(int i=0;i<tokens.length-1;i++)
			{
				if(ExpressionLanguage.isObject(tokens[i]))
					params.put(tokens[i+1], tokens[i]);
			}
		}
		return params;
	}

}
